//---------------------------- //Utility defines (define "Included" (= Infinity (count Steps (step Orthogonal (to if:(not (= (id #1) (who at:(to)))))) (site) (difference (sites Outer) ) ) ) ) (define "RawTerritoryOf" (forEach (difference (sites Occupied by:(player (id #1)))) if:("Included" #1) ) ) (define "TC" // Territory Count (size Array (array (forEach (difference (sites Occupied by:#1)) if:(and ("Included" #1) (is Empty (site)) ) ) ) ) ) //-------------------- // Square scoring utilities (define "IsSiteInSameTerritory" (!= Infinity (count Steps (step Orthogonal (to if:(not (= (id #1) (who at:(to)))))) (from) (site) ) ) ) (define "Coordinates" (results from:(to) to:(forEach (sites Board) if:("IsSiteInSameTerritory" #1)) #2 ) ) (define "IsLowestReachableSite" (= Infinity (count Steps (step Orthogonal (to if:(not (= (id #1) (who at:(to)))))) (site) (sites (results from:(site) to:(forEach (sites Board) if:(< (from) (site))) (to) ) ) ) ) ) (define "RCSQ" //region count - Square (+ (results from:(forEach (difference (sites Occupied by:(player (id #1)))) if:(and ("Included" #1) ("IsLowestReachableSite" #1) )) to:(from) (+ 1 (max (- (max ("Coordinates" #1 (column of:(to)))) (min ("Coordinates" #1 (column of:(to)))) ) (- (max ("Coordinates" #1 (row of:(to)))) (min ("Coordinates" #1 (row of:(to)))) ) ) ) ) ) ) (define "RCHX" //region count - Hex (size Array (array (forEach (difference (sites Occupied by:#1)) if:(and ("Included" #1) (= Infinity (count Steps (step Orthogonal (to if:(not (= (id #1) (who at:(to)))))) (site) (sites (results from:(site) to:(forEach if:(< (from) (site))) (to) ) ) ) ) ) ) ) ) ) (define "GetTerritorySitesAndScore" (and { (set Value at:(handSite #1 0) ("TC" #1)) (set Value at:(handSite #1 1) ( #1)) (set Score #1 (* ("TC" #1) ( #1) )) }) ) (define "RabbitMove" (forEach Piece (do (set Var "SitesMoverAroundFrom" (count Sites in:(sites Around (from) Orthogonal if:(is Mover (who at:(to)))))) next:(move Hop Adjacent (between (range 0 Infinity) if:True) (to (sites Empty) if:(is Empty (to)) ) ) ifAfterwards:(or (< (var "SitesMoverAroundFrom") (count Sites in:(sites Around (last To) Orthogonal if:(is Mover (who at:(to))))) ) (and (= (var "SitesMoverAroundFrom") (count Sites in:(sites Around (last To) Orthogonal if:(is Mover (who at:(to))))) ) (> (count Sites in:(sites Around (last From) Orthogonal if:(is Next (who at:(to))))) (count Sites in:(sites Around (last To) Orthogonal if:(is Next (who at:(to))))) ) ) ) (then (and { ("GetTerritorySitesAndScore" Mover) ("GetTerritorySitesAndScore" Next) (if (is Prev Next) (moveAgain) ) }) ) ) ) ) //----------------------------------------------- // Main routine (game "Rabbit Warrens" (players 2) (equipment { (board use:Cell) (hand P1 size:2) (hand P2 size:2) (piece "Ball" Each) (piece "" Each) (piece "" Neutral) }) (rules (start { (place "1" (handSite P1 0) value:0) (place "2" (handSite P2 0) value:0) (place "0" (handSite P1 1) value:0) (place "0" (handSite P2 1) value:0) }) (play (or (move Pass) "RabbitMove" ) ) (end { (if (and (= (score P1) (score P2)) (all Passed) ) (byScore {(score Mover 0)}) // (score Next (score Next))}) ) (if (all Passed) (byScore) ) }) ) ) //-------------------------------------- // End conditions //------------------------------------------------- // Options (option "Board Type" args:{ } { (item "Hex (6,8)" <(hex Hexagon 6 8)> <(sites Inner)> <(place "Ball1" {2 6 9 13 17 21 25 29 33 37 38 42 46 53 57 61 64 68 72 77 81 85 89 93 97 101 108 112 113 117 121 125})> <(place "Ball2" {1 5 11 15 18 22 26 27 31 35 41 45 49 51 55 59 65 69 73 75 79 83 88 92 99 103 105 109 115 119 120 124})> <"RCHX"> "Rabbit Warrens (hex 6,8). Scoring: Product of the number of distinct enclosures, times the total number of empty sites within the enclosures.")*** (item "Square (10)" <(square 10)> <(sites Inner)> <(place "Ball1" { 6 8 11 13 19 25 27 31 33 39 45 47 52 54 60 66 68 72 74 80 86 88 91 93})> <(place "Ball2" {1 3 10 16 18 22 24 30 36 38 42 44 55 57 61 63 69 75 77 81 83 89 96 98})> <"RCSQ"> "Rabbit Square (order 10). Scoring: Product of the sum of the largest dimension of each distinct enclosure, times the total number of empty sites within the enclosures.")* } ) //--------------------------------------------- (define "ColourBackground" (colour 245 245 245)) (metadata (info { (description "This game was designed to meet a challenge of making a territorial game without placement or capture. It also is a loop-forming game, as territory is defined as the area contained within loops - but the goal is not first to make a loop, but rather territory scoring. The scoring is unique as far as I know. The score is the product of two factors: The first factor is the count of all the empty spaces fully surrounded by the player's pieces (edges don't count as an enclosure) The second factor is the number of distinct regions that a player's pieces enclose. A region includes both empty sites and sites occupied by the opponent. As a player fills a space to subdivide it, one factor is traded off for the other. The board is scaled to be half full of each player having roughly the square of the number of pieces needed to enclose an area equal to the pieces used to enclose it. Movement is designed to progressively increase density. This moves pieces into contact to form loops, subdivided them, and in the extreme, to fill them. Players determine when their score is optimized and pass successively to end the game. For flexibility, of movement, pieces can also move to sites of equal friendly density, which makes the game a race with the potential for cooperative cycles. To prevent cold positions that would cause forced cycling, there is a secondary restriction for this case: pieces most move to have fewer adjacent enemies. This makes filling an opponent's territory difficult, or impossible if one doesn't already have a presence there. It also means pieces within or associated with enemy territory have high mobility, and may be used to create or expand territories in the end game (but often at the cost of significantly increasing the other player's score.) tiebreaker is the last to play loses, in keeping with the nature of a race for the highest score. This game was designed in consultation with Alexander Brady.") (rules "Goal: Player with the largest, most complex 'warren' wins. A warren is the entire networks of friendly stones, that separate areas of the board (enclosures) from each other and from external areas in contact with the board edges. -- Each distinct enclosure may contain any mixture of empty and enemy occupied sites. -- The sites of an enclosure cannot reach any additional site or reach an edge of the board, except by crossing friendly stones or the connections between them. The score is the product of warren complexity and free-space. The actual calculation depends on the board grid: see 'Options', below. Play: The player of the light stones starts by moving a single stone. Thereafter play alternates with each play performing two stone-moves on his turn. Each stone move is in one of the 6 grid directions, either -- to an empty cell surrounded by more friendly stones than before, or -- alternatively, to an empty cell surrounded by the same number of friendly stones, but fewer other neighbors. The moving stone may travel any distance, and pass over any number of stones of either player when moving. A player may pass part or all of his turn. When both players successively pass their entire turns, the game ends and is scored. The player with the higher score wins. If the scores are the same, the last player to move, loses.") (id "1761") (version "1.3.12") (classification "experimental") (author "Dale W. Walton") (credit "Dale W. Walton") (date "02-11-2021") } ) (graphics { (player Colour P1 (colour 250 188 157)) (player Colour P2 (colour Burgundy)) (piece Scale "Ball" 0.93) (piece Colour P1 strokeColour:(colour VeryDarkGrey)) (piece Colour P2 strokeColour:(colour DarkGrey)) (piece Background "Ball" image:"Disc" fillColour:(colour 0 0 0 75) edgeColour:(colour 0 0 0 0) scale:0.95 offsetX:1 offsetY:1.5) (piece Foreground P2 "Ball" image:"Hare-alt2" fillColour:(colour 0 0 0 150) edgeColour:(colour 240 180 150 180) scale:0.7) (piece Foreground P1 "Ball" image:"Hare-alt2" fillColour:(colour 255 255 255 150) edgeColour:(colour 120 90 75 180) scale:0.7) (board Colour Phase0 (colour 250 250 230 90)) (board StyleThickness InnerEdges 1.2) (board StyleThickness OuterEdges 1.6) (board StyleThickness InnerVertices 0.45) (board StyleThickness OuterVertices 0.45) (board Colour InnerVertices (colour Grey)) (board Colour OuterVertices (colour Grey)) (board Colour InnerEdges (colour Black)) (board Colour OuterEdges (colour Black)) (region Colour (sites Outer) (colour 200 250 230 90)) (region Colour ("RawTerritoryOf" P1) (colour 240 180 150 90)) (region Colour ("RawTerritoryOf" P2) (colour 70 0 0 90)) (piece Colour P1 "" fillColour:(colour 240 180 150 90)) (piece Colour P2 "" fillColour:(colour 70 0 0 150)) (piece Colour Neutral "" fillColour:(colour White) strokeColour:(colour DarkGrey)) (show Piece Value "" Middle scale:0.70 offsetX:.25 offsetY:.25) } ) (ai (heuristics (score))) )